package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.api.dto.leanring.LearningRecordFormDTO;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.AssertUtils;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.utils.LearningRecordDelayTaskHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 *
 * @author 丁真
 * @since 2025-06-28
 */
@Service
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {

    private final ILearningLessonService lessonService;
    private final CourseClient courseClient;
    private final LearningRecordDelayTaskHandler taskHandler;

    /**
     * 1、査询当前用户指定课程的学习进度-供course课程微服务运程调用
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {
        //1、根据userId 和 courseId 查询课表信息
        Long userId = UserContext.getUser();
        LearningLesson lesson = lessonService.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        if (ObjectUtil.isNull(lesson)) {
            return null;
        }

        //2、根据lessonId 查询课表下的 学习记录
        List<LearningRecord> list = this.lambdaQuery()
                .eq(LearningRecord::getLessonId, lesson.getId())
                .list();
        // 3、处理redis中的实时进度
        Map<Long,LearningRecord> recordMap = taskHandler.getLearningRecord4LessonId(lesson.getId());
        for (LearningRecord record : list) {
            LearningRecord learningRecord = recordMap.get(record.getSectionId());
            if (ObjectUtils.isNotNull(learningRecord) && ObjectUtils.isNotNull(learningRecord.getMoment())){
                record.setMoment(learningRecord.getMoment());
            }

        }


        // 4、组装DTO并返回
        return LearningLessonDTO.of(lesson.getId(), lesson.getLatestSectionId(), BeanUtils.copyToList(list, LearningRecordDTO.class));
    }

    /**
     * 提交学习记录
     */
    @Override
    public void submitLearningRecord(LearningRecordFormDTO formDTO) {
        //1.处理学习记录
        Boolean FirstFinished = formDTO.getSectionType() == SectionType.VIDEO.getValue() ? handleVideoRecord(formDTO) : handleExamRecord(formDTO);
        if (!FirstFinished || formDTO.getDuration() == -1) {
            return;
        }

        //2.处理课表数据
        handleLearningLesson(formDTO, FirstFinished);

    }

    /**
     * 处理课表数据
     *
     * @param formDTO
     * @param firstFinished
     */
    private void handleLearningLesson(LearningRecordFormDTO formDTO, Boolean firstFinished) {
        //1、查询课表数据(已学习小节数)
        LearningLesson lesson = lessonService.getById(formDTO.getLessonId());
        AssertUtils.isNotNull(lesson, "课表信息不存在,用户没有学习此课程");

        // 2、查询课程数据，判断是否全部学完所有小节
        boolean allLearned = false;
        if (firstFinished) {
            // 2.1、查询课程数据
            CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
            AssertUtils.isNotNull(cInfo, "课程信息不存在,无法更新数据");

            // 2.2、比较(已完成小节数 +1 >= 课程总小节数
            allLearned = lesson.getLearnedSections() + 1 >= cInfo.getSectionNum();

        }

        // 更新课表数据
        boolean update = lessonService.lambdaUpdate()
                .set(LearningLesson::getLatestSectionId, formDTO.getSectionId())
                .set(LearningLesson::getLatestLearnTime, formDTO.getCommitTime())
                .setSql(firstFinished, "learned_sections = learned_sections + 1")
                .set(allLearned, LearningLesson::getStatus, LessonStatus.FINISHED)
                .eq(LearningLesson::getId, formDTO.getLessonId())
                .update();
        AssertUtils.isTrue(update, "更新课表数据失败");


    }

    /**
     * 处理考试记录
     *
     * @param formDTO
     * @return
     */
    private Boolean handleExamRecord(LearningRecordFormDTO formDTO) {
        //1.填充数据
        LearningRecord record = BeanUtils.toBean(formDTO, LearningRecord.class);
        record.setUserId(UserContext.getUser());
        record.setFinished(true);
        record.setFinishTime(formDTO.getCommitTime());

        //2.保存
        boolean save = save(record);
        if (!save) {
            throw new DbException("新增考试记录失败");
        }
        //3.返回是否第一次学习标识
        return true;
    }

    /**
     * 处理视频记录
     *
     * @param formDTO
     * @return
     */
    private Boolean handleVideoRecord(LearningRecordFormDTO formDTO) {
        //1、查询旧的学习记录
        LearningRecord oldRecord = queryOldRecord(formDTO.getLessonId(), formDTO.getSectionId());

        // 2、判断学习记录是否存在
        if (ObjectUtil.isNull(oldRecord)) {
            // 3、如果不存在，新增学习记录
            //3.1.填充数据
            LearningRecord record = BeanUtils.toBean(formDTO, LearningRecord.class);
            record.setUserId(UserContext.getUser());

            //3.2.保存
            boolean save = save(record);
            if (!save) {
                throw new DbException("新增学习记录失败");
            }
            //3.3.返回是否第一次学习标识
            return true;
        }

        // 4、如果存在，更新学习记录
        // 4.1、判断是否是第一次学完当前小节 true表示是第一次 false表示不是第一次
        boolean firstFinished = !oldRecord.getFinished() && oldRecord.getMoment() << 1 > formDTO.getDuration();
        // 4.2、如果是非第一次学完，将学习记录暂存到redis，并且提交一个延时任务
        if (!firstFinished) {
            LearningRecord learningRecord = new LearningRecord();
            learningRecord.setId(oldRecord.getId());
            learningRecord.setLessonId(oldRecord.getLessonId());
            learningRecord.setSectionId(oldRecord.getSectionId());
            learningRecord.setMoment(formDTO.getMoment());
            learningRecord.setFinished(oldRecord.getFinished());
            taskHandler.addLearningRecordDelayCheckTask(learningRecord);
            formDTO.setDuration(-1);
            return false;
        }

        // 4.3更新学习记录(一定要更新的字段:moment，第一次学完时更新的两个字段:finished、finish time))
        boolean update = this.lambdaUpdate()
                .set(LearningRecord::getMoment, formDTO.getMoment())
                .set(firstFinished, LearningRecord::getFinished, true)
                .set(firstFinished, LearningRecord::getFinishTime, formDTO.getCommitTime())
                .eq(LearningRecord::getId, oldRecord.getId())
                .update();
        if (!update) {
            throw new DbException("更新学习记录失败");
        }
        // 4.4、清理redis缓存中的学习记录（目的：保证redis和mysql的双写一致性）
        taskHandler.cleanLearningRecordCache(oldRecord.getSectionId(), oldRecord.getLessonId());

        return true;
    }

    /**
     * 查询旧学习记录
     *
     * @param lessonId
     * @param sectionId
     * @return
     */
    private LearningRecord queryOldRecord(Long lessonId, Long sectionId) {
        // 1、先从缓存中查询学习记录
        LearningRecord oldRecord = taskHandler.readLearningRecordFromCache(lessonId, sectionId);

        // 2、判断缓存中是否存在数据，如果存在，直接返回
        if (ObjectUtil.isNotEmpty(oldRecord)) {
            return oldRecord;
        }

        // 3、如果不存在，则查询数据库，并将结果写入缓存
        // select * from learning_record where lesson_id = ? and section_id = ?
        oldRecord = this.lambdaQuery()
                .eq(LearningRecord::getLessonId, lessonId)
                .eq(LearningRecord::getSectionId, sectionId)
                .one();
        if (ObjectUtil.isNull(oldRecord)) {
            return null;
        }
        // 4、写入缓存
        taskHandler.writeLearningRecordToCache(oldRecord);
        // 5、返回学习记录

        return oldRecord;
    }


}
